﻿using Ardalis.GuardClauses;
using DDD.Application.Servieces.Dtos.MinIODtos;
using DDD.Core.Vaildate;
using Microsoft.Extensions.Logging;
using Minio;
using Minio.DataModel.Args;

namespace DDD.Application.Servieces
{
    public sealed class MinIOService
    {
        private readonly IMinioClient _minioClient;
        private readonly ILogger<MinIOService> _logger;

        public MinIOService(IMinioClient minioClient, ILogger<MinIOService> logger)
        {
            _minioClient = minioClient;
            _logger = logger;
        }

        public async Task CreateBucket(string name)
        {
            Guard.Against.ValueIsNotNullOrEmpty(name);
            var bktExistArg = new BucketExistsArgs().WithBucket(name);

            var isExist = await _minioClient.BucketExistsAsync(bktExistArg);
            if (!isExist)
            {
                var mkBktArg = new MakeBucketArgs().WithBucket(name);
                await _minioClient.MakeBucketAsync(mkBktArg);
                _logger.LogInformation($"{DateTime.Now},MinIO创建桶{name}");
            }
        }

        /// <summary>
        /// 获取所有的桶
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<string>> GetAllBuckets()
        {
            var result = await _minioClient.ListBucketsAsync();

            return result.Buckets.Select(c => c.Name);
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        public async Task<string> UploadFile(UploadFileInput input)
        {
            if (!File.Exists(input.FileName))
                throw new FileNotFoundException(input.FileName);

            PutObjectArgs args = new PutObjectArgs()
                .WithBucket(input.Bucktet)
                .WithFileName(input.FileName)
                .WithContentType(input.ContentType)
                .WithObject(input.FilePath);

            var response = await _minioClient.PutObjectAsync(args);

            return response.Etag;
        }

        /// <summary>
        /// 上传文件流
        /// </summary>
        /// <returns></returns>
        public async Task<string> UploadFileSteam(UploadFileSteamInput input)
        {
            if (!File.Exists(input.FileName))
                throw new FileNotFoundException(input.FileName);

            using var fileStream = new FileStream(input.FileName, FileMode.Open);

            PutObjectArgs args = new PutObjectArgs()
                .WithBucket(input.Bucktet)
                .WithObject(input.FilePath)
                .WithStreamData(fileStream)
                .WithObjectSize(fileStream.Length)
                .WithContentType(input.ContentType);
            var response = await _minioClient.PutObjectAsync(args);

            return response.Etag;
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<Byte[]> DownloadFile(DownloadFileInput input)
        {
            var statObjectArg = new StatObjectArgs()
                .WithObject(input.FilePath)
                .WithBucket(input.Bucket);

            var stateObjectResponse = await _minioClient.StatObjectAsync(statObjectArg);
            if (stateObjectResponse.Size == 0)
                throw new FileNotFoundException(input.FilePath);

            using MemoryStream memoryStream = new MemoryStream();
            GetObjectArgs getObjectArgs = new GetObjectArgs()
                .WithBucket(input.Bucket)
                .WithObject(input.FilePath)
                //.WithFile($"images/{input.FileName}")
                .WithCallbackStream(async st =>
                {
                    await st.CopyToAsync(memoryStream);
                });

            await _minioClient.GetObjectAsync(getObjectArgs);

            return memoryStream.ToArray();
        }

        /// <summary>
        /// 查看桶里所有的文件
        /// </summary>
        /// <param name="bucket"></param>
        /// <returns></returns>
        public async Task<List<string>> GetAllFiles(string bucket)
        {
            ListObjectsArgs args = new ListObjectsArgs()
                .WithBucket(bucket);

            var response = _minioClient.ListObjectsAsync(args);
            List<string> result = new();
            response.Subscribe(c =>
            {
                result.Add(c.Key);
            });

            await Task.Delay(1000);

            return result;
        }

        /// <summary>
        /// 复制文件
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task CopyFile(CopyFileInput input)
        {
            var copyObjectArg = new CopySourceObjectArgs()
                .WithBucket(input.SourceBucket)
                .WithObject(input.SourceFileName);
            var targetObjectArg = new CopyObjectArgs()
                .WithBucket(input.TargetBucket)
                .WithObject(input.TargetFileName)
                .WithCopyObjectSource(copyObjectArg);

            await _minioClient.CopyObjectAsync(targetObjectArg);
        }

        /// <summary>
        /// 获取bucket的详细信息
        /// </summary>
        /// <param name="bucket"></param>
        /// <returns></returns>
        public async Task<GetBcuketInfoOutput> GetBcuketInfo(string bucket)
        {
            //获取版本
            var versionArgs = new GetVersioningArgs()
                .WithBucket(bucket);
            var versionRespone = await _minioClient.GetVersioningAsync(versionArgs);

            //获取桶的加密数据 需要单独配置
            //var encryArgs=new GetBucketEncryptionArgs()
            //    .WithBucket(bucket);
            //var encryResponse=await _minioClient.GetBucketEncryptionAsync(encryArgs);

            //获取桶的策略 需要配置
            //var policyArgs=new GetPolicyArgs()
            //    .WithBucket(bucket);
            //var policyResponse=await _minioClient.GetPolicyAsync(policyArgs);

            return new GetBcuketInfoOutput()
            {
                Version = versionRespone.Status,
                Encry = string.Empty,
                Policy = string.Empty
            };
        }

        /// <summary>
        /// 移除桶 只能移除空的桶
        /// </summary>
        /// <param name="bucket"></param>
        /// <returns></returns>
        public async Task RemoveBucket(string bucket)
        {
            bool isExist = await _minioClient.BucketExistsAsync(new BucketExistsArgs()
                .WithBucket(bucket));
            if (isExist)
            {
                var removeBucketArgs = new RemoveBucketArgs().WithBucket(bucket);
                await _minioClient.RemoveBucketAsync(removeBucketArgs);
            }
        }

        /// <summary>
        /// 生成一个文件的url
        /// </summary>
        /// <returns></returns>
        public async Task<string> CreatePreFileUri(CreatePreFileUriInput input)
        {
            PresignedGetObjectArgs args = new PresignedGetObjectArgs()
                .WithBucket(input.Bucket)
                .WithObject(input.FileName)
                .WithExpiry(input.ExpireSecond); //如果不指定过期时间 默认是7天

            return await _minioClient.PresignedGetObjectAsync(args);
        }
    }
}
